package zone

import (
	"fmt"

	"github.com/cayleygraph/cayley/graph"
	"github.com/cayleygraph/cayley/quad"
)

// 势力：节点情况，自身属性，当前状态
// 节点：当前状态，自身属性

type item struct {
	Key   string      `quad:"key"`
	Value interface{} `quad:"value"`
}
type Power struct {
	rdfType  struct{} `quad:"@type > ex:Power"`
	ID       quad.IRI `quad:"@id"`
	Property []item   `quad:"property"`
	layout   *Layout
}

func (power *Power) Type() struct{} {
	return power.rdfType
}

func (power *Power) Master() *Node {
	nodes := power.layout.NodesWithPath(power.layout.PathIRI(power.ID).Out(Master))
	if len(nodes) == 0 {
		return nil
	} else {
		return &(nodes[0])
	}
}

func (power *Power) Node(relation string) []Node {
	return power.layout.NodesWithPath(power.layout.PathIRI(power.ID).In(relation))
}

func (power *Power) SetProperty(key string, value interface{}) {
	for k, _ := range power.Property {
		if power.Property[k].Key == key {
			power.Property[k].Value = value
			return
		}
	}
	power.Property = append(power.Property, item{Key: key, Value: value})
}

func (power *Power) Layout() *Layout {
	return power.layout
}

type Node struct {
	rdfType        struct{} `quad:"@type > ex:Node"`
	ID             quad.IRI `quad:"@id"`
	Status         []item   `json:"status"`
	Property       []item   `quad:"property"`
	layout         *Layout
	changeStatus   bool
	changeRelation bool
	changeAttach   bool
	changePowers   []*Power
}

func (node *Node) Type() struct{} {
	return node.rdfType
}

func (node *Node) GetStatus(key string) interface{} {
	for k, _ := range node.Status {
		if node.Status[k].Key == key {
			return node.Status[k].Value
		}
	}
	return nil
}

func (node *Node) GetProperty(key string) interface{} {
	for k, _ := range node.Property {
		if node.Property[k].Key == key {
			return node.Property[k].Value
		}
	}
	return nil
}

func (node *Node) SetStatus(key string, value interface{}) {
	node.changeStatus = true
	for k, _ := range node.Status {
		if node.Status[k].Key == key {
			node.Status[k].Value = value
			return
		}
	}
	node.Status = append(node.Status, item{Key: key, Value: value})
}

func (node *Node) SetProperty(key string, value interface{}) {
	for k, _ := range node.Property {
		if node.Property[k].Key == key {
			node.Property[k].Value = value
			return
		}
	}
	node.Property = append(node.Property, item{Key: key, Value: value})
}

func (node *Node) AddRelation(power *Power, relation string) {
	node.changeRelation = true
	if node.changePowers == nil {
		node.changePowers = make([]*Power, 0)
	}
	if err := node.layout.store.AddQuad(quad.Make(node.ID, relation, power.ID, node.layout.sch.Label)); err != nil {
		fmt.Println(err)
	}

	node.changePowers = append(node.changePowers, power)
}

func (node *Node) RemoveRelation(power *Power, relation string) {
	node.changeRelation = true
	if node.changePowers == nil {
		node.changePowers = make([]*Power, 0)
	}
	if err := node.layout.store.RemoveQuad(quad.Make(node.ID, relation, power.ID, node.layout.sch.Label)); err != nil {
		fmt.Println(err)
	}

	node.changePowers = append(node.changePowers, power)
}

func (node *Node) Attach(id string, predicate string) {
	node.changeAttach = true
	if err := node.layout.store.AddQuad(quad.Make(node.ID, predicate, node.layout.IdWrap(id), node.layout.sch.Label)); err != nil {
		fmt.Println(err)
	}
}

func (node *Node) UnAttach(id string, predicate string) {
	node.changeAttach = true
	if err := node.layout.store.RemoveQuad(quad.Make(node.ID, predicate, node.layout.IdWrap(id), node.layout.sch.Label)); err != nil {
		fmt.Println(err)
	}
}

func (node *Node) AttachCount(predicate string) (int64, error) {
	return node.layout.PathIRI(node.ID).Out(predicate).Iterate(node.layout.dispatcher.context).Count()
}

func (node *Node) Object(predicate string) []string {
	it := node.layout.PathIRI(node.ID).Out(predicate).Iterate(node.layout.dispatcher.context)
	result := []string{}
	if err := it.Each(func(value graph.Value) {
		a := node.layout.store.NameOf(value)
		nativeValue := quad.NativeOf(a)
		result = append(result, nativeValue.(string))
	}); err != nil {
		return nil
	}
	return result
}

func (node *Node) Forward() []Node {
	return node.layout.NodesWithPath(node.layout.PathIRI(node.ID).Out(Link))
}

func (node *Node) Reverse() []Node {
	return node.layout.NodesWithPath(node.layout.PathIRI(node.ID).In(Link))
}

func (node *Node) Power(relation string) []Power {
	return node.layout.PowersWithPath(node.layout.PathIRI(node.ID).Out(relation))
}

func (node *Node) Layout() *Layout {
	return node.layout
}
